﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Drawing.Imaging;
using System.Drawing.Drawing2D;
using System.Threading;

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;

using GDIColor = System.Drawing.Color;
using GDIRect = System.Drawing.Rectangle;
using XNAColor = Microsoft.Xna.Framework.Graphics.Color;

namespace CPXMenuEditor.Controls
{
    public partial class ContentPreview : UserControl
    {
        object selectedContent = null;
        object currentlyBuildingContent = null;
        Bitmap bitmap = null;
        Bitmap currentlyBuildingBitmap;
        GraphicsDevice graphicsDevice = null;
        SpriteBatch spriteBatch = null;
        Thread buildImageThread;

        [Browsable(false)]
        public object SelectedContent
        {
            get { return selectedContent; }
            set
            {
                selectedContent = value;
                CreateBitmap();
                this.Invalidate();
            }
        }

        [Browsable(false)]
        public GraphicsDevice GraphicsDevice
        {
            get { return graphicsDevice; }
            set 
            { 
                graphicsDevice = value;
                if (graphicsDevice != null)
                    spriteBatch = new SpriteBatch(graphicsDevice);
            }
        }

        public ContentPreview()
        {
            InitializeComponent();

            buildImageThread = new Thread(BuildImage);

            PositionProgressBar();
        }

        protected override void OnPaint(PaintEventArgs e)
        {
            base.OnPaint(e);

            if (bitmap == null)
                return;

            GDIRect rect = new GDIRect();
            rect.Width = bitmap.Width;
            rect.Height = bitmap.Height;

            if (bitmap.Width < this.Width)
                rect.X = (this.Width / 2) - (bitmap.Width / 2);
            else
                rect.Width = this.Width;

            if (bitmap.Height < this.Height)
                rect.Y = (this.Height / 2) - (bitmap.Height / 2);
            else
                rect.Height = this.Height;

            e.Graphics.DrawImage(bitmap, rect);
        }

        protected override void OnResize(EventArgs e)
        {
            base.OnResize(e);

            PositionProgressBar();
        }

        private void PositionProgressBar()
        {
            progressBar.Left = 50;
            progressBar.Width = this.Width - 100;
            progressBar.Top = (this.Height / 2) - (progressBar.Height / 2);
        }

        private void CreateBitmap()
        {
            if (buildImageThread.IsAlive)
                return;

            if (bitmap != null)
            {
                bitmap.Dispose();
                bitmap = null;
            }

            if (selectedContent == null)
                return;            

            progressBar.Visible = true;

            do
            {
                currentlyBuildingContent = selectedContent;
                buildImageThread.Start();
                while (buildImageThread.IsAlive)
                    Application.DoEvents();
                buildImageThread = new Thread(BuildImage);
                bitmap = currentlyBuildingBitmap;
            }
            while (selectedContent != currentlyBuildingContent);
            
            progressBar.Visible = false;
        }

        private void BuildImage()
        {
            if (currentlyBuildingContent is Texture2D)
                CreateTextureBitmap(currentlyBuildingContent as Texture2D);
            else if (currentlyBuildingContent is SpriteFont)
                CreateFontBitmap(currentlyBuildingContent as SpriteFont);
            else
                CreateUnsupportedBitmap();
        }

        private void CreateTextureBitmap(Texture2D texture)
        {
            if (texture.Format == SurfaceFormat.Color)
            {
                XNAColor[] texData = new XNAColor[texture.Width * texture.Height];
                texture.GetData<XNAColor>(texData);
                currentlyBuildingBitmap = new Bitmap(texture.Width, texture.Height, PixelFormat.Format32bppArgb);
                for (int y = 0; y < texture.Height; y++)
                    for (int x = 0; x < texture.Width; x++)
                        currentlyBuildingBitmap.SetPixel(x, y, GDIColor.FromArgb((int)texData[y * texture.Width + x].PackedValue));
            }
            else
            {
                using (RenderTarget2D renderTarget = new RenderTarget2D(graphicsDevice, texture.Width, texture.Height, 1, SurfaceFormat.Color))
                {
                    DepthStencilBuffer oldDS = graphicsDevice.DepthStencilBuffer;
                    graphicsDevice.SetRenderTarget(0, renderTarget);
                    graphicsDevice.DepthStencilBuffer = null;
                    graphicsDevice.Clear(XNAColor.TransparentBlack);
                    spriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None);
                    graphicsDevice.SamplerStates[0].MagFilter = TextureFilter.Point;
                    graphicsDevice.SamplerStates[0].MinFilter = TextureFilter.Point;
                    graphicsDevice.SamplerStates[0].MipFilter = TextureFilter.None;
                    spriteBatch.Draw(texture, Vector2.Zero, XNAColor.White);
                    spriteBatch.End();
                    graphicsDevice.SetRenderTarget(0, null);

                    CreateTextureBitmap(renderTarget.GetTexture());
                }                
            }
        }

        private void CreateFontBitmap(SpriteFont font)
        {
            using (RenderTarget2D renderTarget = new RenderTarget2D(graphicsDevice, this.Width, this.Height, 1, SurfaceFormat.Color))
            {
                DepthStencilBuffer oldDS = graphicsDevice.DepthStencilBuffer;
                graphicsDevice.SetRenderTarget(0, renderTarget);
                graphicsDevice.DepthStencilBuffer = null;
                graphicsDevice.Clear(XNAColor.TransparentBlack);
                spriteBatch.Begin(SpriteBlendMode.None, SpriteSortMode.Immediate, SaveStateMode.None);
                spriteBatch.DrawString(font, "ABCDEF\nGHIJKL\nMNOPQR\nSTUVW\nXYZ\n\nabcdef\nhijkl\nmnopq\nstuvw\nxyz", new Vector2(25, 25), XNAColor.Black);
                spriteBatch.End();
                graphicsDevice.SetRenderTarget(0, null);
                graphicsDevice.DepthStencilBuffer = oldDS;

                CreateTextureBitmap(renderTarget.GetTexture());
            }
        }

        private void CreateUnsupportedBitmap()
        {
            using (Bitmap dummyImage = new Bitmap(1, 1))
            {
                using (Graphics graphics = Graphics.FromImage(dummyImage))
                {
                    using (Font font = new Font("Arial", 10f, FontStyle.Regular))
                    {
                        SizeF textSize = graphics.MeasureString("Preview Not Supported", font);
                        currentlyBuildingBitmap = new Bitmap((int)textSize.Width, (int)textSize.Height, PixelFormat.Format32bppArgb);
                        Graphics bitmapGraphics = Graphics.FromImage(currentlyBuildingBitmap);
                        bitmapGraphics.Clear(GDIColor.Transparent);
                        bitmapGraphics.SmoothingMode = SmoothingMode.AntiAlias;
                        bitmapGraphics.CompositingMode = CompositingMode.SourceOver;
                        bitmapGraphics.DrawString("Preview Not Supported", font, Brushes.Black, 0, 0);
                    }
                }
            }
        }
    }
}
